﻿//file: slidewidget.cpp

#include "slidewidget.h"
#include <QDebug>
#include <QScrollBar>
/**
 * @brief 构造函数，完成主界面区的设计与布局
 * @param parent - 父对象
 */
SlideWidget::SlideWidget(QWidget *parent) :
    QWidget(parent), pageCount(0)
{
    setStyleSheet("background-color:transparent;");//设置为透明背景
    setMinimumSize(400, 300);
    //设置 mainWidget 属性，并将 mainWidget 设置为可滚动控件
    scrollArea = new QScrollArea(this);
    mainWidget = new QWidget(this);
    scrollArea->setWidget(mainWidget);
    scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    scrollArea->setAlignment(Qt::AlignHCenter);
    mainWidget->resize(pageCount * width(), height());
    mainWidget->setStyleSheet("background-color:transparent;");

    //滚屏对象，设置了滚屏功能后界面就有滚动效果了
    scroller = QScroller::scroller(scrollArea);
    scroller->setParent(this);
    scroller->grabGesture(scrollArea, QScroller::LeftMouseButtonGesture);
    QScrollerProperties properties = scroller->scrollerProperties();//获取滚屏对象属性
    properties.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor,0.8);  //设置过量拖拽
    //properties.setScrollMetric(QScrollerProperties::DragStartDistance, 20);//设置拖拽距离，至少拖拽该距离后才认为产生拖拽动作
    properties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
    //properties.setScrollMetric(QScrollerProperties::SnapTime, 0.5);//设置滑动的时间，值越大，时间越短
    properties.setScrollMetric(QScrollerProperties::MinimumVelocity, 1);//设置滑动速度
    scroller->setScrollerProperties(properties);

    //布局
    hBoxLayout = new QHBoxLayout(this);
    hBoxLayout->setSpacing(0);
    hBoxLayout->setContentsMargins(0, 0, 0, 0);
    mainWidget->setLayout(hBoxLayout);//mainWidget 中使用水平布局

    //动画
    animation = new QPropertyAnimation(scrollArea->horizontalScrollBar(), "value");

    //信号与槽
    connect(scroller, &QScroller::stateChanged, this, &SlideWidget::StateChangedSlot);
}
/**
 * @brief 析构函数。在此会将 widgetList 中管理的控件全部清理
 */
SlideWidget::~SlideWidget()
{
    if(animation != nullptr) delete animation;
    for(auto i : widgetList)
    {
        hBoxLayout->removeWidget(i);
        delete i;
    }

}

/**
 * @brief 添加需要实现滑动效果的控件
 * @param newPage - 需要实现滑动效果的控件
 */
void SlideWidget::AddPage(QWidget *newPage)
{
    ++pageCount;
    mainWidget->resize(pageCount * width(), height());
    newPage->resize(size());
    hBoxLayout->addWidget(newPage);//将 newPage 添加到布局中
    widgetList.append(newPage);    //新加入的 newPage 统一由 widgetList 进行管理
}

/**
 * @brief 控件大小改变事件
 * @param event
 */
void SlideWidget::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event);
    //调整 scrollArea 和 mainWidget 的大小
    scrollArea->resize(size());
    mainWidget->resize(pageCount * width(), height());
}
/**
 * @brief 用于处理滚屏对象状态的改变，实现滑屏效果。
 * @param newState - 当前滚屏对象的状态
 */
void SlideWidget::StateChangedSlot(QScroller::State newState)
{
/*  //调试，查看当前状态
    if(newState == QScroller::Inactive)
    {
        qDebug() << __FUNCTION__ << "newState == QScroller::Inactive";
    }
    if(newState == QScroller::Pressed)
    {
        qDebug() << __FUNCTION__ << "newState == QScroller::Pressed";
    }
    if(newState == QScroller::Dragging)
    {
        qDebug() << __FUNCTION__ << "newState == QScroller::Dragging";
    }
    if(newState == QScroller::Scrolling)
    {
        qDebug() << __FUNCTION__ << "newState == QScroller::Scrolling";
    }
*/
    static bool dragFlag = false;             //拖拽标志位（true表示已经产生拖拽，false表示没有，或已经处理拖拽动作）
    int hScrollBarPos(0);      //当前水平滚动条的位置
    int distance(0);           //水平滚动条滚动一个屏，所需要移动的距离
    int pageIndex(0);          //mainWidget 当前页的下标(从0开始)

    if(newState == QScroller::Dragging)//判断是否有拖拽动作，若有则置标志位
        dragFlag = true;

    //通过水平滚动条的值，决定是否需要滚到下一个屏
    if(newState == QScroller::Inactive && dragFlag && pageCount > 1)
    {
        hScrollBarPos = scrollArea->horizontalScrollBar()->value();
        distance = (scrollArea->horizontalScrollBar()->maximum()
                   - scrollArea->horizontalScrollBar()->minimum()) / (pageCount - 1);
        pageIndex = hScrollBarPos / distance;

        //拖动的距离超过一个屏宽度的二分之一就可以滑到下一个屏了
        if(hScrollBarPos % distance > distance / 2)
            ++pageIndex;

        //为水平滚动条设置动画，避免滚动的太生硬
        animation->setDuration(400);
        animation->setEasingCurve(QEasingCurve::OutCurve);  //设置变化曲线
        animation->setStartValue(hScrollBarPos);
        animation->setEndValue(pageIndex * width());
        animation->start();

        dragFlag = false;
    }
}
